(import
 (except (rnrs base) let-values map error)
 (only (guile)
       lambda* λ
       current-output-port)
 (fileio)
 (srfi srfi-1)
 ;; let-values
 (srfi srfi-11)
 ;; hash tables
 (srfi srfi-69)
 (ice-9 pretty-print))


(define lines (get-lines-from-file "input"))

(define num-rows (length lines))
(define num-cols (string-length (first lines)))

(define trees
  (let ([trees (make-array 0 num-rows num-cols)])
    (let iter-rows ([lines° lines] [row-ind 0])
      (cond
       [(null? lines°) trees]
       [else
        (let iter-cols ([numbers-line° (first lines°)]
                        [col-ind 0])
          (unless (string-null? numbers-line°)
            (array-set! trees
                        (string->number (substring numbers-line° 0 1))
                        row-ind
                        col-ind)
            (iter-cols (substring numbers-line° 1)
                       (+ col-ind 1))))
        (iter-rows (drop lines° 1)
                   (+ row-ind 1))]))))

(define step-down
  (λ (row col)
    (values (+ row 1) col)))

(define step-left
  (λ (row col)
    (values row (- col 1))))

(define step-up
  (λ (row col)
    (values (- row 1) col)))

(define step-right
  (λ (row col)
    (values row (+ col 1))))

(define array-len-in-dim
  (λ (arr dim)
    (let* ([shape (array-shape arr)]
           [dim-min-max (list-ref shape dim)])
      (+ (- (second dim-min-max)
            (first dim-min-max))
         1))))

(define count-visible-trees-in-direction
  (λ (trees init-row-ind init-col-ind step)
    (let ([tree-height (array-ref trees init-row-ind init-col-ind)])
      ;; already step away 1 step towards the border
      (let-values ([(next-row-ind next-col-ind) (step init-row-ind init-col-ind)])
        (let iter ([row-ind° next-row-ind] [col-ind° next-col-ind] [counter 0])
          (cond
           [(array-in-bounds? trees row-ind° col-ind°)
            (if (>= (array-ref trees row-ind° col-ind°) tree-height)
                (+ counter 1)
                (let-values ([(next-row-ind next-col-ind) (step row-ind° col-ind°)])
                  (iter next-row-ind
                        next-col-ind
                        (+ counter 1))))]
           [else counter]))))))

(define directional-scores
  (λ (trees row-ind col-ind)
    (list (count-visible-trees-in-direction trees row-ind col-ind step-up)
          (count-visible-trees-in-direction trees row-ind col-ind step-right)
          (count-visible-trees-in-direction trees row-ind col-ind step-down)
          (count-visible-trees-in-direction trees row-ind col-ind step-left))))

(define score
  (λ (trees row-ind col-ind)
    (apply *
           (filter (λ (s) (not (= s 0)))
                   (directional-scores trees row-ind col-ind)))))

(define calc-tree-visibility
  (λ (trees)
    (let* ([rows (array-len-in-dim trees 0)]
           [cols (array-len-in-dim trees 1)]
           [trees-score (make-array 0 rows cols)]
           [max-row-ind (- rows 1)]
           [max-col-ind (- cols 1)])
      (let iter-rows ([row-ind° 0])
        (cond
         [(<= row-ind° max-row-ind)
          (let iter-cols ([col-ind° 0])
            (cond
             [(<= col-ind° max-col-ind)
              (array-set! trees-score
                          (score trees row-ind° col-ind°)
                          row-ind°
                          col-ind°)
              (iter-cols (+ col-ind° 1))]
             [else
              (iter-rows (+ row-ind° 1))]))]
         [else trees-score])))))

(simple-format
 (current-output-port)
 "~a\n"
 (apply max
        (map (λ (row) (apply max row))
             (array->list (calc-tree-visibility trees)))))
